# !/usr/bin/env python3
# -*- coding: utf-8 -*-


import xml.etree.ElementTree as EleTree
from util.lib import pretty_xml, get_node_type
from util.topo_adapter import get_topo_set
from util.flow_adapter import get_route_path
from util.solution_adapter import get_plan_set


def _init_inject_set(route_set):
    inject_set = []
    for route_ele in route_set:
        inject_set.append({'flow_id': int(route_ele['flow_id']),
                           'first_hop_src_node_id': int(route_ele['src_node_id']),
                           'first_hop_src_port_id': int(route_ele['src_port_id']),
                           'first_hop_link_id': int(route_ele['first_hop_link_id'])
        })
    return inject_set


def _update_slot_to_inject_set(inject_set, plan_set):
    for flow in inject_set:
        # 这里需要获取第一跳节点的link_id
        first_hop_link_id = flow['first_hop_link_id']

        flow_id = flow['flow_id']
        slot_set = []
        for link in plan_set:
            if link['link_id'] == first_hop_link_id and link['flow_id'] == flow_id:
                slot_set.append(link['macrotick_id'])
        flow.update({'slot_set': slot_set})
    return


def _merge_inject_set(inject_set):
    """
    将所有交换节点，即第一跳src_node_id相同的inject_set的元素集合到一起，并按照slot排序
    """
    first_hop_src_node_set = []
    for flow in inject_set:
        if flow['first_hop_src_node_id'] not in first_hop_src_node_set:
            first_hop_src_node_set.append(flow['first_hop_src_node_id'])

    inject_set_tmp = inject_set
    inject_set = [{'node_id': node_id,
                   'inject_set': []} for node_id in first_hop_src_node_set]

    for entry in inject_set:
        for flow in inject_set_tmp:
            for i in range(len(flow['slot_set'])):
                if flow['first_hop_src_node_id'] == entry['node_id']:
                    entry['inject_set'].append({'inject_slot': flow['slot_set'][i],
                                                'dbuf_id': flow['flow_id'],
                                                'valid': 1})
    for node in inject_set:
        node['inject_set'].sort(key=lambda x: x['inject_slot'])

    return inject_set


def _init_inject_tbl_xml(inject_set, inject_tbl_xml):
    inject_set.sort(key=lambda x: x['node_id'])

    root = EleTree.Element('inject_tbl')
    for entry in inject_set:
        node = EleTree.SubElement(root, 'node')
        node_id = EleTree.SubElement(node, 'node_id')
        node_id.text = str(entry['node_id'])
        for item in entry['inject_set']:
            en = EleTree.SubElement(node, 'entry')
            valid = EleTree.SubElement(en, 'valid')
            valid.text = str(item['valid'])
            reserved = EleTree.SubElement(en, 'reserved')
            reserved.text = '0'
            dbuf_id = EleTree.SubElement(en, 'dbuf_id')
            dbuf_id.text = str(item['dbuf_id'])
            time_slot = EleTree.SubElement(en, 'time_slot')
            time_slot.text = str(item['inject_slot'])
    tree = EleTree.ElementTree(root)
    pretty_xml(root, '\t', '\n')
    tree.write(inject_tbl_xml,
               encoding='utf-8',
               xml_declaration=True)
    return


def assemble_inject_tbl(plan_xml: str,
                        topo_xml: str,
                        flow_xml: str,
                        para_xml: str,
                        inject_tbl_xml):

    # 1.根据topo_xml，获取topo_set
    topo_set = get_topo_set(topo_xml)
    print("1. topo_set: ")
    [print(entry) for entry in topo_set]
    # 2.获取流的路由路径
    route_set = get_route_path(flow_xml, topo_set)
    print("2. route_set: ")
    [print(entry) for entry in route_set]
    # 3.根据para_xml，获取设备节点的类型，node_type_set
    node_type_set = get_node_type(para_xml)
    print("3. node_type_set: ")
    [print(entry) for entry in node_type_set]
    # 4.根据plan_xml，获取plan_set
    plan_set = get_plan_set(plan_xml)
    print("4. plan_set: ")
    [print(entry) for entry in plan_set]

    # 4.从flow_xml中获取所有ST流的第一跳的first_hop_src_node_id、first_hop_src_port_id、first_hop_link_id，即inject_set
    inject_set = _init_inject_set(route_set)
    print("4. inject_set: ")
    [print(entry) for entry in inject_set]

    # 5.将slot更新到inject_set
    _update_slot_to_inject_set(inject_set, plan_set)
    print("5. update slot to inject_set: ")
    [print(entry) for entry in inject_set]

    # 6.将所有交换节点，即第一跳src_node_id相同的inject_set的元素集合到一起，并按照slot排序
    inject_set = _merge_inject_set(inject_set)
    print("6. merge inject_set: ")
    [print(entry) for entry in inject_set]

    # 生成xml
    _init_inject_tbl_xml(inject_set, inject_tbl_xml)
    return


def main():
    plan_xml = '../input/solution.xml'
    topo_xml = '../input/topo_feature.xml'
    flow_xml = '../input/flow_feature.xml'
    para_xml = '../input/cfg_parameter.xml'

    inject_tbl_xml = '../output/tbl/inject_tbl.xml'
    assemble_inject_tbl(plan_xml,
                        topo_xml,
                        flow_xml,
                        para_xml,
                        inject_tbl_xml)
    return


if __name__ == '__main__':
    main()
